﻿using System.IO;

namespace maranov.SymlinkManager.FilesystemLinks
{
    /// <summary>
    /// Hard link.
    /// </summary>
    public partial class HardLink : Link, IHardLink
    {
        /// <summary>
        /// Hard link.
        /// Allows creation from both existing and nonexisting links.
        /// </summary>
        /// <param name="name">Hard link name in a filesystem.</param>
        /// <param name="target">Hard link's target name in a filesystem.</param>
        public HardLink(string name, string target) :
            base(
                // Path is always stored in absolute format.
                Path.GetFullPath(name),
                Path.GetFullPath(target)
            )
        { }

        public override void Create()
        {
            if (!Exists &&
                Target.Exists)
            {
                // Create hardlink
                if (!CreateHardLink(Name.Name, Target.Name, CreateHardlinkSecurityAttributes))
                    throw new LinkCreationException("Hard link creation failed.", GetLastError());
            }
            else
            {
                if (Exists)
                {
                    if (State.HasFlag(LinkState.FullLink))
                        throw new LinkCreationException("Hard link already exists in the link's location.");
                    else
                        throw new LinkCreationException("Other file already exists in the link's location.");
                }
                else
                {
                    throw new LinkCreationException("Target file does not exist.");
                }
            }
        }

        public override void Remove(bool force = false)
        {
            if (force ||
                State.HasFlag(LinkState.FullLink))
            {
                RemoveHardlink(Name.Name);
            }
            else
            {
                if (State.HasFlag(LinkState.LinkExists))
                    throw new LinkRemovalException("Target name is not the specified hard link and cannot be removed as one.");
                else
                    throw new LinkRemovalException("Hard link does not exist in specified location.");
            }
        }

        protected override bool ValidateLink()
        {
            return (State.HasFlag(LinkState.FullLink));
        }

        protected override bool ValidateLinkConnection()
        {
            try
            {
                using (FileStream target = new FileStream(Target.Name, FileMode.Open, FileAccess.Read))
                using (FileStream source = new FileStream(Name.Name, FileMode.Open, FileAccess.Read))
                {
                    var targetHandle = target.SafeFileHandle.DangerousGetHandle();
                    var sourceHandle = source.SafeFileHandle.DangerousGetHandle();

                    var targetInfo = new BY_HANDLE_FILE_INFORMATION();
                    var sourceInfo = new BY_HANDLE_FILE_INFORMATION();

                    if (!GetFileInformationByHandle(targetHandle, out targetInfo))
                        return false;
                    if (!GetFileInformationByHandle(sourceHandle, out sourceInfo))
                        return false;

                    return 
                        (targetInfo.FileIndexHigh == sourceInfo.FileIndexHigh &&
                        targetInfo.FileIndexLow == sourceInfo.FileIndexLow);
                }
            }
            catch(IOException)
            {
                return false;
            }
        }
    }
}
